package cn.blunce.weather.view;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;

import cn.blunce.weather.R;

public class PullRefreshView extends LinearLayout implements View.OnTouchListener {

    public static final int STATUS_PULLING = 0;
    public static final int STATUS_RELEASE_TO_REFRESH = 1;
    public static final int STATUS_REFRESHING = 2;
    public static final int STATUS_REFRESH_FINISHED = 3;

    private int mCurrentStatus = STATUS_REFRESH_FINISHED;

    /**
     * speed of the scrolling back
     */
    public static final int SCROLL_SPEED = -20;


    public static final long ONE_MINUTE = 60 * 1000;
    public static final long ONE_HOUR = 60 * ONE_MINUTE;
    public static final long ONE_DAY = 24 * ONE_HOUR;
    public static final long ONE_MONTH = 30 * ONE_DAY;
    public static final long ONE_YEAR = 12 * ONE_MONTH;

    private static final String UPDATED_TIME = "update_time";

    private PullToRefreshListener mListener;

    private SharedPreferences preferences;

    private View mHeader;
    private ScrollView mScrollView;
    private ProgressBar mProgressBar;
    private ImageView mArrow;
    private TextView mDescription;
    private TextView mUpdateTime;

    private MarginLayoutParams mHeaderLayoutParams;

    private int mId = -1;

    private int hideHeaderHeight;

    private int lastStatus = mCurrentStatus;

    private float yDown;

    private int touchSlop;

    private boolean loadOnce;
    private boolean ableToPull;

    public PullRefreshView(Context context, AttributeSet attrs) {
        super(context, attrs);
        preferences = PreferenceManager.getDefaultSharedPreferences(context);
        mHeader = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_head, null, true);
        mProgressBar = (ProgressBar) mHeader.findViewById(R.id.progress_bar);
        mArrow = (ImageView) mHeader.findViewById(R.id.arrow);
        mDescription = (TextView) mHeader.findViewById(R.id.description);
        mUpdateTime = (TextView) mHeader.findViewById(R.id.updated_at);
        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        setRefreshTime();
        setOrientation(VERTICAL);
        addView(mHeader, 0);

        mHeaderLayoutParams = (MarginLayoutParams) mHeader.getLayoutParams();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed && !loadOnce) {
            hideHeaderHeight = -mHeader.getHeight();
            mHeaderLayoutParams.topMargin = hideHeaderHeight;
            mScrollView = (ScrollView) getChildAt(1);
            mScrollView.setOnTouchListener(this);
            loadOnce = true;
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        setIsAbleToPull(event);
        if (ableToPull) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    yDown = event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float yMove = event.getRawY();
                    int distance = (int) (yMove - yDown);
                    if (distance <= 0 && mHeaderLayoutParams.topMargin <= hideHeaderHeight) {
                        return false;
                    }
                    if (distance < touchSlop) {
                        return false;
                    }
                    if (mCurrentStatus != STATUS_REFRESHING) {
                        if (mHeaderLayoutParams.topMargin > 0) {
                            mCurrentStatus = STATUS_RELEASE_TO_REFRESH;
                        } else {
                            mCurrentStatus = STATUS_PULLING;
                        }
                        mHeaderLayoutParams.topMargin = (distance / 2) + hideHeaderHeight;
                        mHeader.setLayoutParams(mHeaderLayoutParams);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                default:
                    if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
                        new RefreshingTask().execute();
                    } else if (mCurrentStatus == STATUS_PULLING) {
                        new HideHeaderTask().execute();
                    }
                    break;
            }
            if (mCurrentStatus == STATUS_PULLING
                    || mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
                updateHeaderView();
                mScrollView.setPressed(false);
                mScrollView.setFocusable(false);
                mScrollView.setFocusableInTouchMode(false);
                lastStatus = mCurrentStatus;
                return true;
            }
        }
        return false;
    }

    public void setOnRefreshListener(PullToRefreshListener listener, int id) {
        mListener = listener;
        mId = id;
    }


    private void setIsAbleToPull(MotionEvent event) {
        View firstChild = mScrollView.getChildAt(0);
        if (firstChild != null) {
            int y = mScrollView.getScrollY();
            if (y <= 0) {
                if (!ableToPull) {
                    yDown = event.getRawY();
                }
                ableToPull = true;
            } else {
                if (mHeaderLayoutParams.topMargin != hideHeaderHeight) {
                    mHeaderLayoutParams.topMargin = hideHeaderHeight;
                    mHeader.setLayoutParams(mHeaderLayoutParams);
                }
                ableToPull = false;
            }
        } else {
            ableToPull = true;
        }
    }

    private void updateHeaderView() {
        if (lastStatus != mCurrentStatus) {
            if (mCurrentStatus == STATUS_PULLING) {
                mDescription.setText(R.string.pull_to_refresh);
                mArrow.setVisibility(View.VISIBLE);
                mProgressBar.setVisibility(View.GONE);
                rotateArrow();
            } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
                mDescription.setText(R.string.release_to_refresh);
                mArrow.setVisibility(View.VISIBLE);
                mProgressBar.setVisibility(View.GONE);
                rotateArrow();
            } else if (mCurrentStatus == STATUS_REFRESHING) {
                mDescription.setText(R.string.refreshing);
                mProgressBar.setVisibility(View.VISIBLE);
                mArrow.clearAnimation();
                mArrow.setVisibility(View.GONE);
            }
            setRefreshTime();
        }
    }

    private void rotateArrow() {
        float pivotX = mArrow.getWidth() / 2f;
        float pivotY = mArrow.getHeight() / 2f;
        float fromDegrees = 0f;
        float toDegrees = 0f;
        if (mCurrentStatus == STATUS_PULLING) {
            fromDegrees = 180f;
            toDegrees = 360f;
        } else if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {
            fromDegrees = 0f;
            toDegrees = 180f;
        }
        RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
        animation.setDuration(100);
        animation.setFillAfter(true);
        mArrow.startAnimation(animation);
    }

    private void setRefreshTime() {
        long lastUpdateTime = preferences.getLong(UPDATED_TIME + mId, -1);
        long currentTime = System.currentTimeMillis();
        long timePassed = currentTime - lastUpdateTime;
        long timeIntoFormat;
        String updateAtValue;
        if (lastUpdateTime == -1) {
            updateAtValue = getResources().getString(R.string.not_updated_yet);
        } else if (timePassed < 0) {
            updateAtValue = getResources().getString(R.string.time_error);
        } else if (timePassed < ONE_MINUTE) {
            updateAtValue = getResources().getString(R.string.updated_just_now);
        } else if (timePassed < ONE_HOUR) {
            timeIntoFormat = timePassed / ONE_MINUTE;
            String value = timeIntoFormat + "分钟";
            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
        } else if (timePassed < ONE_DAY) {
            timeIntoFormat = timePassed / ONE_HOUR;
            String value = timeIntoFormat + "小时";
            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
        } else if (timePassed < ONE_MONTH) {
            timeIntoFormat = timePassed / ONE_DAY;
            String value = timeIntoFormat + "天";
            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
        } else if (timePassed < ONE_YEAR) {
            timeIntoFormat = timePassed / ONE_MONTH;
            String value = timeIntoFormat + "个月";
            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
        } else {
            timeIntoFormat = timePassed / ONE_YEAR;
            String value = timeIntoFormat + "年";
            updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
        }
        mUpdateTime.setText(updateAtValue);
    }

    /**
     * 正在刷新的任务，在此任务中会去回调注册进来的下拉刷新监听器。
     *
     * @author guolin
     */
    class RefreshingTask extends AsyncTask<Void, Integer, Void> {

        @Override
        protected Void doInBackground(Void... params) {
            int topMargin = mHeaderLayoutParams.topMargin;
            while (true) {
                topMargin = topMargin + SCROLL_SPEED;
                if (topMargin <= 0) {
                    topMargin = 0;
                    break;
                }
                publishProgress(topMargin);
                try {
                    wait(10);
                } catch (Exception e) {
                }
            }
            mCurrentStatus = STATUS_REFRESHING;
            publishProgress(0);
            if (mListener != null) {
                mListener.onRefresh();
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... topMargin) {
            updateHeaderView();
            mHeaderLayoutParams.topMargin = topMargin[0];
            mHeader.setLayoutParams(mHeaderLayoutParams);
        }

    }

    /**
     * 隐藏下拉头的任务，当未进行下拉刷新或下拉刷新完成后，此任务将会使下拉头重新隐藏。
     *
     * @author guolin
     */
    class HideHeaderTask extends AsyncTask<Void, Integer, Integer> {

        @Override
        protected Integer doInBackground(Void... params) {
            int topMargin = mHeaderLayoutParams.topMargin;
            while (true) {
                topMargin = topMargin + SCROLL_SPEED;
                if (topMargin <= hideHeaderHeight) {
                    topMargin = hideHeaderHeight;
                    break;
                }
                publishProgress(topMargin);
                try {
                    wait(10);
                } catch (Exception e) {
                }
            }
            return topMargin;
        }

        @Override
        protected void onProgressUpdate(Integer... topMargin) {
            mHeaderLayoutParams.topMargin = topMargin[0];
            mHeader.setLayoutParams(mHeaderLayoutParams);
        }

        @Override
        protected void onPostExecute(Integer topMargin) {
            mHeaderLayoutParams.topMargin = topMargin;
            mHeader.setLayoutParams(mHeaderLayoutParams);
            mCurrentStatus = STATUS_REFRESH_FINISHED;
        }
    }

    /**
     * 下拉刷新的监听器，使用下拉刷新的地方应该注册此监听器来获取刷新回调。
     *
     * @author guolin
     */
    public interface PullToRefreshListener {

        /**
         * 刷新时会去回调此方法，在方法内编写具体的刷新逻辑。注意此方法是在子线程中调用的， 你可以不必另开线程来进行耗时操作。
         */
        void onRefresh();

    }

    public void update() {
        mHeaderLayoutParams.topMargin = 0;
        new RefreshingTask().execute();
    }

    public void finishRefreshing() {
        mCurrentStatus = STATUS_REFRESH_FINISHED;
        preferences.edit().putLong(UPDATED_TIME + mId, System.currentTimeMillis()).commit();
        new HideHeaderTask().execute();
    }
}
